unit uXML ;

interface

uses
    Windows,
    Messages,
    SysUtils,
    Variants,
    Classes,
    Graphics,
    Controls,
    Forms,
    Dialogs,
    Contnrs,
    uBasic,
    uObjects,
    uViewSchematic,
    uViewPCB,
    uVisitor,
    sdXmlDocuments,
    GDIPAPI,
    GDIPOBJ ;

type
    TToXMLVisitor = class( TMedaVisitor )
    private
        fXMLDoc : TsdXmlDocument ;
        fNode : TXMLNode ;
        fLibrary : boolean ;
        procedure DoGraphics( aNode : TXMLNode ; Instance : TMedaObject ) ;
        procedure DoContainer( Instance : TMedaContainer ) ;
    public
        function VisitNode( Instance : TMedaViewable ) : TObject ; override ;

        // viewers
        function VisitNode( Instance : TMedaPCBLayer ) : TObject ; override ;
        function VisitNode( Instance : TMedaPCBInterface ) : TObject ; override ;
        function VisitNode( Instance : TMedaSchematicPage ) : TObject ; override ;
        function VisitNode( Instance : TMedaSchInterface ) : TObject ; override ;

        // objects
        function VisitNode( Instance : TMedaObject ) : TObject ; override ;
        function VisitNode( Instance : TMedaVisible ) : TObject ; override ;
        function VisitNode( Instance : TMedaContainer ) : TObject ; override ;
        function VisitNode( Instance : TMedaVisibleContainer ) : TObject ; override ;
        function VisitNode( Instance : TMedaRatsNest ) : TObject ; override ;
        function VisitNode( Instance : TMedaPart ) : TObject ; override ;
        function VisitNode( Instance : TMedaBank ) : TObject ; override ;
        function VisitNode( Instance : TMedaPin ) : TObject ; override ;
        function VisitNode( Instance : TMedaLabel ) : TObject ; override ;
        function VisitNode( Instance : TMedaPower ) : TObject ; override ;
        function VisitNode( Instance : TMedaPort ) : TObject ; override ;
        function VisitNode( Instance : TMedaNet ) : TObject ; override ;
        function VisitNode( Instance : TMedaLine ) : TObject ; override ;
        function VisitNode( Instance : TMedaTrack ) : TObject ; override ;
        function VisitNode( Instance : TMedaVia ) : TObject ; override ;
        function VisitNode( Instance : TMedaCopper ) : TObject ; override ;
        function VisitNode( Instance : TMedaShape ) : TObject ; override ;
        function VisitNode( Instance : TMedaStack ) : TObject ; override ;
        function VisitNode( Instance : TMedaModule ) : TObject ; override ;
        function VisitNode( Instance : TMedaProject ) : TObject ; override ;

        procedure SaveToFile( aRoot : TMedaViewable ; aFilename : string ) ;
    end ;

    TfrmXML = class( TFrame )
    private
        { Private declarations }
        function DoTransform( s : string ) : TMedaTransform ;
        function DoGraphics( aNode : TXmlNode ) : TGPGraphicsPath ;

        procedure BuildPort( nPort : TXmlNode ; aPart : TMedaPart ) ;
        procedure BuildPin( nPin : TXmlNode ; aBank : TMedaBank ) ;
        procedure BuildBank( nBank : TXmlNode ; aPart : TMedaPart ) ;
        procedure BuildStack( nStack : TXmlNode ; aShape : TMedaShape ) ;
        procedure BuildShape( nShape : TXmlNode ; aPart : TMedaPart ) ;
        procedure BuildPart( nPart : TXmlNode ; aModule : TMedaModule ) ;

        procedure BuildLine( nLine : TXmlNode ; aNet : TMedaNet ) ;
        procedure BuildLabel( nLabel : TXmlNode ; aNet : TMedaNet ) ;
        procedure BuildTrack( nTrack : TXmlNode ; aNet : TMedaNet ) ;
        procedure BuildVia( nVia : TXmlNode ; aNet : TMedaNet ) ;
        procedure BuildNet( nNet : TXmlNode ; aModule : TMedaModule ) ;
    public
        { Public declarations }
        procedure LoadProject( aProject : TMedaProject ; aFilename : string ) ;
        function ImportECPinPakBank( aPart : TMedaPart ; aFilename : string ) : TMedaBank ;
        function ImportShape( iPart : TMedaPart ; aFilename : string ) : TMedaShape ;

        procedure LoadFromFile( aObject : TMedaViewable ; aFilename : string ) ;
        procedure SaveToFile( aObject : TMedaViewable ; aFilename : string ) ;
    end ;

implementation

{$R *.dfm}

const
    PathPointTypeStart = 0 ;
    PathPointTypeLine = 1 ;
    PathPointTypeBezier = 3 ;
    PathPointTypePathTypeMask = $07 ;
    PathPointTypePathDashMode = $10 ;
    PathPointTypePathMarker = $20 ;
    PathPointTypeCloseSubpath = $80 ;
    PathPointTypeBezier3 = 3 ;

    fmt1            : string = '%.6g' ;
    fmt2            : string = '%.6g,%.6g' ;

    { TfrmXML }

function TfrmXML.DoGraphics( aNode : TXmlNode ) : TGPGraphicsPath ;
var
    iGraph          : integer ;
    nItem           : TXmlNode ;
    iPoints         : array of TGPPointF ;
    iTypes          : array of Byte ;
    n, t            : integer ;
begin
    n := 0 ;
    try
        Result := nil ;
        for iGraph := 0 to aNode.NodeCount - 1 do begin
            nItem := aNode[ iGraph ] ;
            t := PathPointTypeStart ;
            if LowerCase( nItem.Name ) = 'start' then
            else if LowerCase( nItem.Name ) = 'line' then
                t := PathPointTypeLine
            else if LowerCase( nItem.Name ) = 'bezier' then
                t := PathPointTypeBezier
            else
                Continue ;
            Setlength( iPoints, n + 1 ) ;
            iPoints[ n ].X := StrToFloatDef( nItem.AttributeByName( 'x' ), 0 ) ;
            iPoints[ n ].Y := StrToFloatDef( nItem.AttributeByName( 'y' ), 0 ) ;
            Setlength( iTypes, n + 1 ) ;
            if LowerCase( nItem.AttributeByName( 'dashed' ) ) = 'yes' then
                t := t or PathPointTypePathDashMode ;
            if LowerCase( nItem.AttributeByName( 'marker' ) ) = 'yes' then
                t := t or PathPointTypePathMarker ;
            if LowerCase( nItem.AttributeByName( 'closed' ) ) = 'yes' then
                t := t or PathPointTypeCloseSubpath ;

            iTypes[ n ] := t ;
            inc( n ) ;
        end ;
        Result := TGPGraphicsPath.Create( PGPPointF( @iPoints[ 0 ] ), PByte( @iTypes[ 0 ] ), n ) ;
    finally
        iPoints := nil ;
        iTypes := nil ;
    end ;
end ;

function TfrmXML.DoTransform( s : string ) : TMedaTransform ;
var
    v               : single ;
    c, i            : integer ;
begin
    Val( s, v, c ) ;
    Result.Position.X := v ;
    Delete( s, 1, c ) ;
    Val( s, v, c ) ;
    Result.Position.Y := v ;
    Delete( s, 1, c ) ;
    Val( s, i, c ) ;
    Result.Rotation := TMedaDir( i ) ;
    Delete( s, 1, c ) ;
    Val( s, i, c ) ;
    Result.Mirror := i <> 0 ;
end ;

procedure TfrmXML.BuildStack( nStack : TXmlNode ; aShape : TMedaShape ) ;
var
    aStack          : TMedaStack ;
    sPadType        : string ;
    od, w, h        : single ;
    x, y            : single ;
begin
    aStack := aShape.AddStack ;
    aStack.Name := nStack.AttributeByName( 'name' ) ;
    aStack.Designator := nStack.AttributeByName( 'ref' ) ;
    aStack.SetUniqueID( StrToIntDef( nStack.AttributeByName( 'uid' ), 0 ) ) ;
    sPadType := nStack.AttributeByName( 'type' ) ;
    if sPadType = 'circle' then begin
        od := StrToFloatDef( nStack.AttributeByName( 'od' ), 0.0 ) ;
        aStack.Build( ptyCircle, [ od ] ) ;
    end
    else if sPadType = 'octagon' then begin
        od := StrToFloatDef( nStack.AttributeByName( 'od' ), 0.0 ) ;
        aStack.Build( ptyOcta, [ od ] ) ;
    end
    else if sPadType = 'rect' then begin
        w := StrToIntDef( nStack.AttributeByName( 'w' ), 0 ) ;
        h := StrToIntDef( nStack.AttributeByName( 'h' ), 0 ) ;
        aStack.Build( ptyRect, [ w, h ] ) ;
    end
    else if sPadType = 'oval' then begin
        w := StrToIntDef( nStack.AttributeByName( 'w' ), 0 ) ;
        h := StrToIntDef( nStack.AttributeByName( 'h' ), 0 ) ;
        aStack.Build( ptyOblong, [ w, h ] ) ;
    end ;
    x := StrToIntDef( nStack.AttributeByName( 'x' ), 0 ) ;
    y := StrToIntDef( nStack.AttributeByName( 'y' ), 0 ) ;
    aStack.Offset := MakePoint( x, y ) ;
end ;

procedure TfrmXML.BuildShape( nShape : TXmlNode ; aPart : TMedaPart ) ;
var
    aShape          : TMedaShape ;
    iTrans          : TMedaTransform ;
    iStack          : integer ;
    nStack          : TXmlNode ;
    uLayer          : DWORD ;
begin
    aShape := aPart.AddShape ;
    aShape.Name := nShape.AttributeByName( 'name' ) ;
    aShape.Designator := nShape.AttributeByName( 'ref' ) ;
    aShape.SetUniqueID( StrToIntDef( nShape.AttributeByName( 'uid' ), 0 ) ) ;
    uLayer := 0 ;
    for iStack := 0 to nShape.NodeCount - 1 do begin
        nStack := nShape[ iStack ] ;
        uLayer := StrToIntDef( nStack.AttributeByName( 'place' ), 0 ) ;
        if LowerCase( nStack.Name ) = 'stack' then
            BuildStack( nStack, aShape ) ;
    end ;
    if uLayer > 0 then begin
        iTrans := DoTransform( nShape.AttributeByName( 'trans' ) ) ;
        aShape.PlaceAtLayer( uLayer, iTrans ) ;
    end ;
end ;

procedure TfrmXML.BuildPin( nPin : TXmlNode ; aBank : TMedaBank ) ;
var
    aPin            : TMedaPin ;
    x, y            : single ;
    sDir, sPol, sName, sDes : string ;
    iType           : TMedaPinType ;
    aDir            : TMedaDir ;
begin
    // <Pin port="413" type="0" dir="2" off="-120,-10" flags="0111" uid="381"/>
    aPin := aBank.AddPin ;
    aPin.SetUniqueID( StrToIntDef( nPin.AttributeByName( 'uid' ), 0 ) ) ;
    sName := nPin.AttributeByName( 'name' ) ;
    sDes := nPin.AttributeByName( 'ref' ) ;
    sDir := nPin.AttributeByName( 'dir' ) ;
    aDir := TMedaDir( StrToIntDef( sPol, 0 ) ) ;
    sPol := nPin.AttributeByName( 'pol' ) ;
    iType := TMedaPinType( StrToIntDef( sPol, 0 ) ) ;
    aPin.Build( sName, sDes, aDir, False, iType ) ;
    x := StrToFloatDef( nPin.AttributeByName( 'x' ), 0 ) ;
    y := StrToFloatDef( nPin.AttributeByName( 'y' ), 0 ) ;
    aPin.Offset := MakePoint( x, y ) ;
end ;

procedure TfrmXML.BuildBank( nBank : TXmlNode ; aPart : TMedaPart ) ;
var
    aBank           : TMedaBank ;
    iPin            : integer ;
    iTrans          : TMedaTransform ;
    nPin            : TXmlNode ;
    uPage           : DWORD ;
begin
    // <Bank name="PM01" ref="A" trans="240,400,0,0" flags="0111" uid="380">
    aBank := aPart.AddBank ;
    aBank.Name := nBank.AttributeByName( 'name' ) ;
    aBank.Designator := nBank.AttributeByName( 'ref' ) ;
    aBank.SetUniqueID( StrToIntDef( nBank.AttributeByName( 'uid' ), 0 ) ) ;
    for iPin := 0 to nBank.NodeCount - 1 do begin
        nPin := nBank[ iPin ] ;
        if LowerCase( nPin.Name ) = 'pin' then
            BuildPin( nPin, aBank )
        else if LowerCase( nPin.Name ) = 'graphics' then
            aBank.Path := DoGraphics( nPin ) ;
    end ;
    uPage := StrToIntDef( nBank.AttributeByName( 'place' ), 0 ) ;
    if uPage > 0 then begin
        iTrans := DoTransform( nBank.AttributeByName( 'trans' ) ) ;
        aBank.PlaceAtPage( uPage, iTrans ) ;
    end ;
end ;

procedure TfrmXML.BuildPort( nPort : TXmlNode ; aPart : TMedaPart ) ;
var
    aPort           : TMedaPort ;
begin
    aPort := aPart.AddPort ;
    aPort.Name := nPort.AttributeByName( 'name' ) ;
    aPort.Designator := nPort.AttributeByName( 'ref' ) ;
    aPort.SetUniqueID( StrToIntDef( nPort.AttributeByName( 'uid' ), 0 ) ) ;
end ;

procedure TfrmXML.BuildPart( nPart : TXmlNode ; aModule : TMedaModule ) ;
var
    aPart           : TMedaPart ;
    iSub, iPort, iBank, iShape : integer ;
    nSub, nPort, nBank, nShape : TXmlNode ;
begin
    // <Part name="MSP430F1132IRHBT" ref="U1" uid="23">
    //    <Nets uid="24">
    //      <Port name="RST_L_NMI" ref="5" flags="0111" pin="28" stack="293" uid="60" uid="61"/>
    //    </Nets>
    //    <Banks uid="25">
    //      <Bank name="PM01" ref="A" trans="240,400,0,0" flags="0111" uid="27">
    //        <Pin port="60" type="0" dir="2" off="-120,-10" flags="0111" uid="28"/>
    //      </Bank>
    //    </Banks>
    //    <Footprints uid="26">
    //      <Footprint name="SQFP50P700X900-32N" ref="" trans="500,1000,0,0" flags="0111" uid="285">
    //        <Stack port="216" type="4" off="-124.015754699707,88.5826797485352" flags="0100" uid="289"/>
    //      </Footprint>
    //    </Footprints>
    //  </Part>
    aPart := aModule.AddPart ;
    aPart.Name := nPart.AttributeByName( 'name' ) ;
    aPart.Designator := nPart.AttributeByName( 'ref' ) ;
    aPart.SetUniqueID( StrToIntDef( nPart.AttributeByName( 'uid' ), 0 ) ) ;
    for iSub := 0 to nPart.NodeCount - 1 do begin
        nSub := nPart[ iSub ] ;
        if LowerCase( nSub.Name ) = 'nets' then begin
            for iPort := 0 to nSub.NodeCount - 1 do begin
                nPort := nSub[ iPort ] ;
                if LowerCase( nPort.Name ) = 'port' then
                    BuildPort( nPort, aPart ) ;
            end ;
        end
        else if LowerCase( nSub.Name ) = 'banks' then begin
            for iBank := 0 to nSub.NodeCount - 1 do begin
                nBank := nSub[ iBank ] ;
                if LowerCase( nBank.Name ) = 'bank' then
                    BuildBank( nBank, aPart ) ;
            end ;
        end
        else if LowerCase( nSub.Name ) = 'footprints' then begin
            for iShape := 0 to nSub.NodeCount - 1 do begin
                nShape := nSub[ iShape ] ;
                if LowerCase( nShape.Name ) = 'footprint' then
                    BuildShape( nShape, aPart ) ;
            end ;
        end ;
    end ;
end ;

procedure TfrmXML.BuildLine( nLine : TXmlNode ; aNet : TMedaNet ) ;
var
    aLine           : TMedaLine ;
    nPoly           : TXmlNode ;
    iPoly           : integer ;
    iTrans          : TMedaTransform ;
    x, y            : single ;
    uPage           : DWORD ;
begin
    aLine := aNet.AddLine ;
    aLine.Name := nLine.AttributeByName( 'name' ) ;
    aLine.Designator := nLine.AttributeByName( 'id' ) ;
    aLine.SetUniqueID( StrToIntDef( nLine.AttributeByName( 'uid' ), 0 ) ) ;
    if nLine.NodeCount > 0 then begin
        nPoly := nLine[ 0 ] ;
        for iPoly := 1 to nPoly.AttributeCount div 2 do begin
            x := StrToIntDef( nPoly.AttributeByName( 'dx' + IntToStr( iPoly ) ), 0 ) ;
            y := StrToIntDef( nPoly.AttributeByName( 'dy' + IntToStr( iPoly ) ), 0 ) ;
            aLine.NewPoint( MakePoint( x, y ) ) ;
        end ;
    end ;
    uPage := StrToIntDef( nLine.AttributeByName( 'place' ), 0 ) ;
    if uPage > 0 then begin
        iTrans := DoTransform( nLine.AttributeByName( 'trans' ) ) ;
        aLine.PlaceAtPage( uPage, iTrans ) ;
    end ;
end ;

procedure TfrmXML.BuildLabel( nLabel : TXmlNode ; aNet : TMedaNet ) ;
var
    aLabel          : TMedaLabel ;
    iTrans          : TMedaTransform ;
    uPage           : DWORD ;
begin
    aLabel := aNet.AddLabel ;
    aLabel.Name := nLabel.AttributeByName( 'name' ) ;
    aLabel.Designator := nLabel.AttributeByName( 'id' ) ;
    aLabel.SetUniqueID( StrToIntDef( nLabel.AttributeByName( 'uid' ), 0 ) ) ;
    uPage := StrToIntDef( nLabel.AttributeByName( 'place' ), 0 ) ;
    if uPage > 0 then begin
        iTrans := DoTransform( nLabel.AttributeByName( 'trans' ) ) ;
        aLabel.PlaceAtPage( uPage, iTrans ) ;
    end ;
end ;

procedure TfrmXML.BuildTrack( nTrack : TXmlNode ; aNet : TMedaNet ) ;
var
    aTrack          : TMedaTrack ;
    nPoly           : TXmlNode ;
    iPoly           : integer ;
    iTrans          : TMedaTransform ;
    x, y            : single ;
    uLayer          : DWORD ;
begin
    aTrack := aNet.AddTrack ;
    aTrack.Name := nTrack.AttributeByName( 'name' ) ;
    aTrack.Designator := nTrack.AttributeByName( 'id' ) ;
    aTrack.Width := StrToFloatDef( nTrack.AttributeByName( 'width' ), aTrack.Width ) ;
    aTrack.SetUniqueID( StrToIntDef( nTrack.AttributeByName( 'uid' ), 0 ) ) ;
    if nTrack.NodeCount > 0 then begin
        nPoly := nTrack[ 0 ] ;
        for iPoly := 1 to nPoly.AttributeCount div 2 do begin
            x := StrToIntDef( nPoly.AttributeByName( 'dx' + IntToStr( iPoly ) ), 0 ) ;
            y := StrToIntDef( nPoly.AttributeByName( 'dy' + IntToStr( iPoly ) ), 0 ) ;
            aTrack.NewPoint( MakePoint( x, y ) ) ;
        end ;
    end ;
    uLayer := StrToIntDef( nTrack.AttributeByName( 'place' ), 0 ) ;
    if uLayer > 0 then begin
        iTrans := DoTransform( nTrack.AttributeByName( 'trans' ) ) ;
        aTrack.PlaceOnLayer( uLayer, iTrans ) ;
    end ;
end ;

procedure TfrmXML.BuildVia( nVia : TXmlNode ; aNet : TMedaNet ) ;
var
    aVia            : TMedaVia ;
    nPoly           : TXmlNode ;
    iPoly           : integer ;
    iTrans          : TMedaTransform ;
    x, y            : single ;
    uPage           : DWORD ;
begin
    aVia := aNet.AddVia ;
    aVia.Name := nVia.AttributeByName( 'name' ) ;
    aVia.Designator := nVia.AttributeByName( 'id' ) ;
    aVia.SetUniqueID( StrToIntDef( nVia.AttributeByName( 'uid' ), 0 ) ) ;
    uPage := StrToIntDef( nVia.AttributeByName( 'place' ), 0 ) ;
    if uPage > 0 then begin
        iTrans := DoTransform( nVia.AttributeByName( 'trans' ) ) ;
        aVia.PlaceAtPage( uPage, iTrans ) ;
    end ;
end ;

procedure TfrmXML.BuildNet( nNet : TXmlNode ; aModule : TMedaModule ) ;
var
    aNet            : TMedaNet ;
    iSub, iNode, iLine, iLabel, iTrack, iVia : integer ;
    nSub, nNode, nLine, nLabel, nTrack, nVia : TXmlNode ;
    uNode           : DWORD ;
    aObject         : TMedaObject ;
begin
    aNet := aModule.AddNet ;
    aNet.Name := 'Net' ;
    aNet.Designator := nNet.AttributeByName( 'id' ) ;
    aNet.SetUniqueID( StrToIntDef( nNet.AttributeByName( 'uid' ), 0 ) ) ;
    for iSub := 0 to nNet.NodeCount - 1 do begin
        nSub := nNet[ iSub ] ;
        if LowerCase( nSub.Name ) = 'tree' then begin
            for iNode := 0 to nSub.NodeCount - 1 do begin
                nNode := nSub[ iNode ] ;
                if LowerCase( nNode.Name ) = 'node' then begin
                    uNode := StrToIntDef( nNode.AttributeByName( 'uid' ), 0 ) ;
                    aObject := aModule.FindNodeByUID( uNode ) ;
                    if ( aObject is TMedaPin ) or ( aObject is TMedaStack ) then
                        aNet.ConnectPort( TMedaPort( aObject.Parent ) ) ;
                end ;
            end ;
        end
        else if LowerCase( nSub.Name ) = 'lines' then begin
            for iLine := 0 to nSub.NodeCount - 1 do begin
                nLine := nSub[ iLine ] ;
                if LowerCase( nLine.Name ) = 'line' then
                    BuildLine( nLine, aNet ) ;
            end ;
        end
        else if LowerCase( nSub.Name ) = 'labels' then begin
            for iLabel := 0 to nSub.NodeCount - 1 do begin
                nLabel := nSub[ iLabel ] ;
                if LowerCase( nLabel.Name ) = 'label' then
                    BuildLabel( nLabel, aNet ) ;
            end ;
        end
        else if LowerCase( nSub.Name ) = 'tracks' then begin
            for iTrack := 0 to nSub.NodeCount - 1 do begin
                nTrack := nSub[ iTrack ] ;
                if LowerCase( nTrack.Name ) = 'track' then
                    BuildTrack( nTrack, aNet ) ;
            end ;
        end
        else if LowerCase( nSub.Name ) = 'vias' then begin
            for iVia := 0 to nSub.NodeCount - 1 do begin
                nVia := nSub[ iVia ] ;
                if LowerCase( nVia.Name ) = 'via' then
                    BuildVia( nVia, aNet ) ;
            end ;
        end ;
    end ;
end ;

procedure TfrmXML.LoadProject( aProject : TMedaProject ; aFilename : string ) ;
var
    aXMLDoc         : TsdXmlDocument ;
    nModule,
        nSub,
        nPart, nNet,
        nPage,
        nLayer      : TXmlNode ;

    aModule         : TMedaModule ;
    aPage           : TMedaSchematicPage ;
    aLT             : TMedaLayerType ;

    iModule,
        iSub,
        iPart, iNet,
        iPage,
        iLayer      : integer ;
begin
    aXMLDoc := TsdXmlDocument.Create ;
    with aXMLDoc do try
        LoadFromFile( aFilename ) ;
        if LowerCase( Root.Name ) = 'project' then begin
            aProject.Name := Root.AttributeByName( 'name' ) ;
            aProject.SetUniqueID( StrToIntDef( Root.AttributeByName( 'uid' ), 0 ) ) ;
            for iModule := 0 to Root.NodeCount - 1 do begin
                nModule := Root[ iModule ] ;
                if LowerCase( nModule.Name ) = 'schematic' then begin
                    for iPage := 0 to nModule.NodeCount - 1 do begin
                        nPage := nModule[ iPage ] ;
                        aPage := vsbSchematicViewer.AddPage ;
                        aPage.Name := nPage.AttributeByName( 'name' ) ;
                        aPage.SetUniqueID( StrToIntDef( nPage.AttributeByName( 'uid' ), 0 ) ) ;
                    end ;
                end
                else if LowerCase( nModule.Name ) = 'pcb' then begin
                    for iLayer := 0 to nModule.NodeCount - 1 do begin
                        nLayer := nModule[ iLayer ] ;
                        aLT := TMedaLayerType( StrToIntDef( nLayer.AttributeByName( 'type' ), 0 ) ) ;
                        vsbPCBViewer.Layers[ aLT ].SetUniqueID( StrToIntDef( nLayer.AttributeByName( 'uid' ), 0 ) ) ;
                    end ;
                end
                else if LowerCase( nModule.Name ) = 'module' then begin
                    aModule := aProject.NewModule ;
                    aModule.Name := nModule.AttributeByName( 'name' ) ;
                    aModule.SetUniqueID( StrToIntDef( nModule.AttributeByName( 'uid' ), 0 ) ) ;
                    for iSub := 0 to nModule.NodeCount - 1 do begin
                        nSub := nModule[ iSub ] ;
                        if LowerCase( nSub.Name ) = 'modules' then begin
                            for iPart := 0 to nSub.NodeCount - 1 do begin
                                nPart := nSub[ iPart ] ;
                                if LowerCase( nPart.Name ) = 'part' then
                                    BuildPart( nPart, aModule ) ;
                            end ;
                        end
                        else if LowerCase( nSub.Name ) = 'nets' then begin
                            for iNet := 0 to nSub.NodeCount - 1 do begin
                                nNet := nSub[ iNet ] ;
                                if LowerCase( nNet.Name ) = 'net' then
                                    BuildNet( nNet, aModule )
                                else if LowerCase( nNet.Name ) = 'port' then
                                    BuildNet( nNet, aModule ) ;
                            end ;
                        end ;
                    end ;
                end ;
            end ;
        end
        else
            raise ExceptionMeda.CreateFmt( 'No project found in root of XML file: %s', [ Root.Name ] )
    finally
        Free ;
    end ;
end ;

function TfrmXML.ImportECPinPakBank( aPart : TMedaPart ; aFilename : string ) : TMedaBank ;
var
    aXMLDoc         : TsdXmlDocument ;
    nNode, nPin     : TXmlNode ;
    aPin            : TMedaPin ;
    sName, sDir     : string ;
    aDir            : TMedaDir ;
    aType           : TMedaPinType ;
    i, j            : integer ;

begin
    Result := nil ;
    aXMLDoc := TsdXmlDocument.Create ;
    with aXMLDoc do try
        LoadFromFile( aFilename ) ;
        if LowerCase( Root.Name ) = 'ecpinpak' then
            for i := 0 to Root.NodeCount - 1 do begin
                nNode := Root[ i ] ;
                if LowerCase( nNode.Name ) = 'instance' then begin
                    // <Instance ObjectID="MSP430F1132IRHBT"></Instance>
                    sName := nNode.AttributeByName( 'ObjectID' ) ;
                    Result := aPart.AddBank ;
                    Result.Name := sName ;
                end
                else if LowerCase( nNode.Name ) = 'pinmap' then begin
                    if assigned( Result ) then begin
                        Result.Name := nNode.AttributeByName( 'id' ) ;
                        // <Pinmap shape="Rectangle" id="PM01">
                        for j := 0 to nNode.NodeCount - 1 do begin
                            nPin := nNode[ j ] ;
                            if LowerCase( nPin.Name ) = 'pin' then begin
                                // <Pin pol="N" name="RST_L_NMI" id="P5" dir="I" num="5" type="undefined"></Pin>
                                sDir := nPin.AttributeByName( 'dir' ) ;
                                aDir := drSouth ;
                                if length( sDir ) > 0 then
                                    case LowerCase( sDir )[ 1 ] of
                                        'i' : aDir := drWest ;
                                        'o' : aDir := drEast ;
                                        'b' : aDir := drNorth ;
                                        'u' : aDir := drSouth ;
                                    end ;
                                aType := TMedaPinType( StrToIntDef( nPin.AttributeByName( 'pol' ), 0 ) ) ;
                                aPin := Result.AddPin ;
                                aPin.Build(
                                    nPin.AttributeByName( 'name' ),
                                    nPin.AttributeByName( 'num' ),
                                    aDir, False, aType ) ;
                            end ;
                        end ;
                        Result.BuildOutline ;
                    end ;
                end ;
            end ;
    finally
        Free ;
    end ;
end ;

function TfrmXML.ImportShape( iPart : TMedaPart ; aFilename : string ) : TMedaShape ;
var
    aXMLDoc         : TsdXmlDocument ;
    aNode, aPart,
        aPath       : TXmlNode ;
    aUnit, aName, aValue,
        aID, aWidth : string ;
    aType, aRef     : string ;
    dUnit, dx, dy   : single ;
    aValues         : array of double ;
    aStack          : TMedaStack ;
    i, j, k, l      : integer ;
    x, y            : single ;
    aStacks         : TObjectlist ;
begin
    dUnit := 1 ;
    aStacks := TObjectlist.Create( True ) ;
    aXMLDoc := TsdXmlDocument.Create ;
    Result := iPart.AddShape ;

    with aXMLDoc do try
        LoadFromFile( aFilename ) ;
        if Root.Name = 'part_database' then
            for i := 0 to Root.NodeCount - 1 do begin
                aNode := Root[ i ] ;
                if aNode.Name = 'database_units' then begin
                    aUnit := aNode.AttributeByName( 'unit' ) ;
                    if aUnit = 'millimeters' then
                        dUnit := 10000 / 254 ;    // millimeters in mils
                end
                else if aNode.Name = 'part_objects' then begin
                    for j := 0 to aNode.NodeCount - 1 do begin
                        aPart := aNode[ j ] ;
                        if aPart.Name = 'part' then begin
                            // <part name="surf_lin_93x24" type="pin" unit="millimeters">
                            aName := aPart.AttributeByName( 'name' ) ;
                            aType := aPart.AttributeByName( 'type' ) ;
                            if aType = 'component' then
                                Result.Name := aName ;
                            for k := 0 to aPart.NodeCount - 1 do begin
                                aPath := aPart[ k ] ;
                                if aPath.Name = 'attribute' then begin
                                end
                                else if aPath.Name = 'circle' then begin
                                    //  <circle id="PAD" x="0.0" y="0.0" r="0.25"/>
                                    aID := aPath.AttributeByName( 'id' ) ;
                                    if aID = 'PAD' then begin
                                        aStack := TMedaStack.Create ;
                                        try
                                            aStack.Name := aName ;
                                            Setlength( aValues, aPath.AttributeCount ) ;
                                            for l := 1 to aPath.AttributeCount - 1 do begin
                                                aValue := aPath.AttributeValue[ l ] ;
                                                aValues[ l ] := StrToFloat( aValue ) * dUnit ;
                                            end ;
                                            aStack.Build( ptyCircle, [ aValues[ 3 ] ] ) ;
                                        finally
                                            aStacks.Add( aStack ) ;
                                        end ;
                                    end ;
                                end
                                else if aPath.Name = 'rectangle' then begin
                                    // <rectangle id="PAD" x0="0.0" y0="0.0" x1="3.0" y1="1.6" r="0"/>
                                    aID := aPath.AttributeByName( 'id' ) ;
                                    if aID = 'PAD' then begin
                                        aStack := TMedaStack.Create ;
                                        try
                                            aStack.Name := aName ;
                                            Setlength( aValues, 2 ) ;
                                            for l := 0 to 1 do begin
                                                aValue := aPath.AttributeValue[ l + 3 ] ;
                                                aValues[ l ] := StrToFloat( aValue ) * dUnit ;
                                            end ;
                                            aStack.Build( ptyRect, aValues ) ;
                                        finally
                                            aStacks.Add( aStack ) ;
                                        end ;
                                    end ;
                                end
                                else if aPath.Name = 'path' then begin
                                    //      <path id="PAD" width="0.45" style="solid" cap="circular">
                                    //        <value v0="0.55"/>
                                    //        <value v1="0.0"/>
                                    //        <value v2="-0.55"/>
                                    //        <value v3="0.0"/>
                                    //      </path>
                                    aID := aPath.AttributeByName( 'id' ) ;
                                    if aID = 'PAD' then begin
                                        aStack := TMedaStack.Create ;
                                        try
                                            aStack.Name := aName ;
                                            aWidth := aPath.AttributeByName( 'width' ) ;
                                            Setlength( aValues, aPath.NodeCount + 1 ) ;
                                            for l := 0 to aPath.NodeCount - 1 do begin
                                                aValue := aPath[ l ].AttributeValue[ 0 ] ;
                                                aValues[ l ] := StrToFloat( aValue ) * dUnit ;
                                            end ;
                                            aValues[ aPath.NodeCount ] := StrToFloat( aWidth ) * dUnit ;
                                            if aValues[ 0 ] - aValues[ 2 ] > aValues[ 1 ] - aValues[ 3 ] then
                                                aStack.Build( ptyOblong, [ abs( aValues[ 0 ] - aValues[ 2 ] ), aValues[ aPath.NodeCount ] ] )
                                            else
                                                aStack.Build( ptyOblong, [ aValues[ aPath.NodeCount ], abs( aValues[ 1 ] - aValues[ 3 ] ) ] ) ;
                                        finally
                                            aStacks.Add( aStack ) ;
                                        end ;
                                    end ;
                                end
                                else if aPath.Name = 'graph' then begin
                                    // <graph id="COMPONENT_PLACEMENT_OUTLINE" attr="">
                                    aID := aPath.AttributeByName( 'id' ) ;
                                    if aID = 'COMPONENT_PLACEMENT_OUTLINE' then begin
                                        Setlength( aValues, aPath.NodeCount ) ;
                                        for l := 0 to aPath.NodeCount - 1 do begin
                                            aValue := aPath[ l ].AttributeValue[ 0 ] ;
                                            aValues[ l ] := StrToFloat( aValue ) * dUnit ;
                                        end ;
                                        Result.Build( aValues ) ;
                                    end ;
                                end
                                else if aPath.Name = 'pin' then begin
                                    // <pin name="1" ref="surf_lin_93x24_j" x="-4.55" y="5.08" r="0" type="no_mirror"/>
                                    aRef := aPath.AttributeByName( 'ref' ) ;
                                    aStack := nil ;
                                    for l := 0 to aStacks.Count - 1 do
                                        if ( aStacks[ l ] as TMedaStack ).Name = aRef then begin
                                            aStack := aStacks[ l ] as TMedaStack ;
                                            Break ;
                                        end ;
                                    if assigned( aStack ) then begin
                                        aStack.Designator := aPath.AttributeByName( 'name' ) ;
                                        dx := StrToFloat( aPath.AttributeByName( 'x' ) ) ;
                                        x := dx * dUnit ;
                                        dy := StrToFloat( aPath.AttributeByName( 'y' ) ) ;
                                        y := dy * dUnit ;
                                        aStack.Offset := MakePoint( x, y ) ;
                                        Result.CloneStack( aStack ) ;
                                    end ;
                                end ;
                            end ;
                        end ;
                    end ;
                end ;
            end ;
    finally
        iPart.UpdatePorts( Result ) ;
        Free ;
        aStacks.Free ;
        aValues := nil ;
    end ;
end ;

procedure TfrmXML.LoadFromFile( aObject : TMedaViewable ; aFilename : string ) ;
begin
    LoadProject( TMedaProject( aObject ), aFilename ) ;
end ;

procedure TfrmXML.SaveToFile( aObject : TMedaViewable ; aFilename : string ) ;
begin
    with TToXMLVisitor.Create do try
        SaveToFile( aObject, aFilename ) ;
    finally
        Free ;
    end ;
end ;

{ TToXMLVisitor }

function TToXMLVisitor.VisitNode( Instance : TMedaViewable ) : TObject ;
begin
    fNode.AttributeAdd( 'uid', Instance.UIDAsString ) ;
    Result := nil ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaObject ) : TObject ;
begin
    fNode.AttributeAdd( 'uid', Instance.UIDAsString ) ;
    Result := nil ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaVisible ) : TObject ;
begin
    if Instance.Placed then
        fNode.AttributeAdd( 'place', Instance.PlaceAsString ) ;
    fNode.AttributeAdd( 'trans', Instance.TransformAsString ) ;
    fNode.AttributeAdd( 'flags', Instance.FlagsAsString ) ;
    Result := VisitNode( TMedaObject( Instance ) ) ;
end ;

procedure TToXMLVisitor.DoContainer( Instance : TMedaContainer ) ;
var
    i               : integer ;
begin
    with Instance do
        for i := 0 to Count - 1 do
            if assigned( Objects[ i ] ) then
                Objects[ i ].Accept( Self ) ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaVisibleContainer ) : TObject ;
begin
    Result := nil ;
    if assigned( Instance ) and ( Instance.Count > 0 ) then begin
        fNode := fNode.NodeNew( Instance.Name ) ;
        try
            Result := VisitNode( TMedaVisible( Instance ) ) ;
            DoContainer( Instance ) ;
        finally
            fNode := fNode.Parent ;
        end ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaContainer ) : TObject ;
begin
    Result := nil ;
    if assigned( Instance ) and ( Instance.Count > 0 ) then begin
        fNode := fNode.NodeNew( Instance.Name ) ;
        try
            Result := VisitNode( TMedaObject( Instance ) ) ;
            DoContainer( Instance ) ;
        finally
            fNode := fNode.Parent ;
        end ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaRatsNest ) : TObject ;
var
    i               : integer ;
begin
    Result := nil ;
    if assigned( Instance ) and ( Instance.Count > 0 ) then begin
        fNode := fNode.NodeNew( Instance.Name ) ;
        try
            Result := VisitNode( TMedaObject( Instance ) ) ;
            with Instance do
                for i := 0 to Count - 1 do
                    if assigned( Objects[ i ] ) then begin
                        fNode := fNode.NodeNew( 'Port' ) ;
                        fNode.AttributeAdd( 'uid', Objects[ i ].UIDAsString ) ;
                        fNode := fNode.Parent ;
                    end ;
        finally
            fNode := fNode.Parent ;
        end ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaPin ) : TObject ;
begin
    fNode := fNode.NodeNew( 'Pin' ) ;
    try
        if not fLibrary and assigned( Instance.Port ) then
            fNode.AttributeAdd( 'port', Instance.Port.UIDAsString )
        else begin
            fNode.AttributeAdd( 'name', Instance.Name ) ;
            fNode.AttributeAdd( 'ref', Instance.Designator ) ;
        end ;
        fNode.AttributeAdd( 'size', Instance.Size ) ;
        fNode.AttributeAdd( 'type', ord( Instance.PinType ) ) ;
        fNode.AttributeAdd( 'dir', IntToStr( Ord( Instance.Direction ) ) ) ;
        fNode.AttributeAdd( 'off', Format( fmt2, [ Instance.Offset.X, Instance.Offset.Y ] ) ) ;
        fNode.AttributeAdd( 'flags', Instance.FlagsAsString ) ;
        Result := VisitNode( TMedaObject( Instance ) ) ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaStack ) : TObject ;
begin
    fNode := fNode.NodeNew( 'Stack' ) ;
    try
        if not fLibrary and assigned( Instance.Port ) then
            fNode.AttributeAdd( 'port', Instance.Port.UIDAsString )
        else begin
            fNode.AttributeAdd( 'name', Instance.Name ) ;
            fNode.AttributeAdd( 'ref', Instance.Designator ) ;
        end ;
        fNode.AttributeAdd( 'type', ord( Instance.PadType ) ) ;
        fNode.AttributeAdd( 'size', Format( fmt2, [ Instance.Size.Width, Instance.Size.Height ] ) ) ;
        fNode.AttributeAdd( 'off', Format( fmt2, [ Instance.Offset.X, Instance.Offset.Y ] ) ) ;
        if Instance.Hole > 0.0 then
            fNode.AttributeAdd( 'hole', Format( fmt1, [ Instance.Hole ] ) ) ;
        fNode.AttributeAdd( 'flags', Instance.FlagsAsString ) ;
        Result := VisitNode( TMedaObject( Instance ) ) ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaLabel ) : TObject ;
begin
    fNode := fNode.NodeNew( 'Label' ) ;
    try
        fNode.AttributeAdd( 'name', Instance.Name ) ;
        fNode.AttributeAdd( 'ref', Instance.Designator ) ;
        Result := VisitNode( TMedaVisible( Instance ) ) ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaPower ) : TObject ;
begin
    fNode := fNode.NodeNew( 'Label' ) ;
    try
        fNode.AttributeAdd( 'name', Instance.Name ) ;
        fNode.AttributeAdd( 'ref', Instance.Designator ) ;
        Result := VisitNode( TMedaVisible( Instance ) ) ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaPort ) : TObject ;
begin
    fNode := fNode.NodeNew( 'Port' ) ;
    try
        fNode.AttributeAdd( 'name', Instance.Name ) ;
        fNode.AttributeAdd( 'ref', Instance.Designator ) ;
        if assigned( Instance.Net ) then
            fNode.AttributeAdd( 'net', Instance.Net.UIDAsString ) ;
        if assigned( Instance.Pin ) then
            fNode.AttributeAdd( 'pin', Instance.Pin.UIDAsString ) ;
        if assigned( Instance.Stack ) then
            fNode.AttributeAdd( 'stack', Instance.Stack.UIDAsString ) ;
        Result := VisitNode( TMedaObject( Instance ) ) ;
        //        Instance.GatherLines( vsbSchematicViewer.CurrentPage ) ;
        DoContainer( Instance ) ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaNet ) : TObject ;
begin
    fNode := fNode.NodeNew( 'Net' ) ;
    try
        fNode.AttributeAdd( 'name', Instance.Name ) ;
        fNode.AttributeAdd( 'ref', Instance.Designator ) ;
        Result := VisitNode( TMedaObject( Instance ) ) ;
        //        Instance.GatherLines( vsbSchematicViewer.CurrentPage ) ;
        DoContainer( Instance ) ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaLine ) : TObject ;
var
    i               : integer ;
begin
    fNode := fNode.NodeNew( 'Line' ) ;
    try
        Result := VisitNode( TMedaVisible( Instance ) ) ;
        for i := 1 to Instance.Count - 1 do begin
            fNode := fNode.NodeNew( 'Point' ) ;
            try
                fNode.AttributeAdd( 'x', Format( '%g', [ Instance.Points[ i ].X ] ) ) ;
                fNode.AttributeAdd( 'y', Format( '%g', [ Instance.Points[ i ].Y ] ) ) ;
            finally
                fNode := fNode.Parent ;
            end ;
        end ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaShape ) : TObject ;
begin
    fNode := fNode.NodeNew( 'Footprint' ) ;
    try
        fNode.AttributeAdd( 'name', Instance.Name ) ;
        fNode.AttributeAdd( 'ref', Instance.Designator ) ;
        Result := VisitNode( TMedaVisible( Instance ) ) ;
        DoContainer( Instance ) ;
        DoGraphics( fNode, Instance ) ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaTrack ) : TObject ;
var
    i               : integer ;
begin
    fNode := fNode.NodeNew( 'Track' ) ;
    try
        fNode.AttributeAdd( 'width', Format( '%g', [ Instance.Width ] ) ) ;
        Result := VisitNode( TMedaVisible( Instance ) ) ;
        for i := 1 to Instance.Count - 1 do begin
            fNode := fNode.NodeNew( 'Point' ) ;
            try
                fNode.AttributeAdd( 'x', Format( '%g', [ Instance.Points[ i ].X ] ) ) ;
                fNode.AttributeAdd( 'y', Format( '%g', [ Instance.Points[ i ].Y ] ) ) ;
            finally
                fNode := fNode.Parent ;
            end ;
        end ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaVia ) : TObject ;
begin
    fNode := fNode.NodeNew( 'Via' ) ;
    try
        fNode.AttributeAdd( 'name', Instance.Name ) ;
        fNode.AttributeAdd( 'ref', Instance.Designator ) ;
        fNode.AttributeAdd( 'hole', Format( '%g', [ Instance.Hole ] ) ) ;
        fNode.AttributeAdd( 'layers', 'all' ) ;
        Result := VisitNode( TMedaObject( Instance ) ) ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaCopper ) : TObject ;
begin
    fNode := fNode.NodeNew( 'Copper' ) ;
    try
        fNode.AttributeAdd( 'name', Instance.Name ) ;
        fNode.AttributeAdd( 'ref', Instance.Designator ) ;
        Result := VisitNode( TMedaObject( Instance ) ) ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaBank ) : TObject ;
begin
    fNode := fNode.NodeNew( 'Bank' ) ;
    try
        fNode.AttributeAdd( 'name', Instance.Name ) ;
        if fLibrary then
        else
            fNode.AttributeAdd( 'ref', Instance.Designator ) ;
        Result := VisitNode( TMedaVisible( Instance ) ) ;
        DoContainer( Instance ) ;
        DoGraphics( fNode, Instance ) ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaPart ) : TObject ;
begin
    fNode := fNode.NodeNew( 'Part' ) ;
    try
        fNode.AttributeAdd( 'name', Instance.Name ) ;
        fNode.AttributeAdd( 'ref', Instance.Designator ) ;
        Result := VisitNode( TMedaObject( Instance ) ) ;
        DoContainer( Instance ) ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaModule ) : TObject ;
begin
    fNode := fNode.NodeNew( 'Module' ) ;
    try
        fNode.AttributeAdd( 'name', Instance.Name ) ;
        fNode.AttributeAdd( 'ref', Instance.Designator ) ;
        Result := VisitNode( TMedaObject( Instance ) ) ;
        DoContainer( Instance ) ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaProject ) : TObject ;
var
    i               : integer ;
begin
    fNode.Name := 'Project' ;
    fLibrary := False ;
    fNode.AttributeAdd( 'name', Instance.Name ) ;
    fNode.AttributeAdd( 'ref', ( Instance as TmedaProject ).Designator ) ;
    Result := VisitNode( TMedaObject( Instance ) ) ;
    vsbSchematicViewer.ProjectView.Accept( Self ) ;
    vsbPCBViewer.ProjectView.Accept( Self ) ;
    for i := 0 to Instance.Count - 1 do
        if assigned( Instance[ i ] ) then
            Instance[ i ].Accept( Self ) ;
end ;

procedure TToXMLVisitor.SaveToFile( aRoot : TMedaViewable ; aFilename : string ) ;
begin
    if assigned( aRoot ) then begin
        fXMLDoc := TsdXmlDocument.Create ;
        try
            fNode := fXMLDoc.Root ;
            fNode.Name := 'Library' ;
            fLibrary := True ;
            aRoot.Accept( Self ) ;
            with fXMLDoc do begin
                ExternalEncoding := se8bit ;
                XmlFormat := xfReadable ;
                CommentString := 'Created by ObjectEDA' ;
                WriteOnDefault := False ;
                SaveToFile( aFilename ) ;
            end ;
        finally
            fXMLDoc.Free ;
        end ;
    end ;
end ;

procedure TToXMLVisitor.DoGraphics( aNode : TXMLNode ; Instance : TMedaObject ) ;
var
    iGraph, iPoint  : TXmlNode ;
    iPath           : TGPGraphicsPath ;
    iIterator       : TGPGraphicsPathIterator ;
    i, n            : integer ;
    iTypes          : array of Byte ;
    iPoints         : array of TGPPointF ;
begin
    iPath := Instance.Path ;
    iIterator := TGPGraphicsPathIterator.Create( iPath ) ;
    try
        n := iIterator.GetCount ;
        if n > 0 then try
            Setlength( iPoints, n ) ;
            Setlength( iTypes, n ) ;
            iIterator.Enumerate( PGPPointF( @iPoints[ 0 ] ), PByte( @iTypes[ 0 ] ), n ) ;
            iGraph := aNode.NodeNew( 'Graphics' ) ;
            iGraph.AttributeAdd( 'count', n ) ;
            if iIterator.HasCurve then
                iGraph.AttributeAdd( 'hascurve', 'yes' ) ;
            for i := 0 to n - 1 do begin
                case iTypes[ i ] and PathPointTypePathTypeMask of
                    PathPointTypeStart : iPoint := iGraph.NodeNew( 'Start' ) ;
                    PathPointTypeLine : iPoint := iGraph.NodeNew( 'Line' ) ;
                    PathPointTypeBezier : iPoint := iGraph.NodeNew( 'Bezier' ) ;
                else
                    Break ;
                end ;
                iPoint.AttributeAdd( 'x', Format( fmt1, [ iPoints[ i ].X ] ) ) ;
                iPoint.AttributeAdd( 'y', Format( fmt1, [ iPoints[ i ].Y ] ) ) ;
                if ( iTypes[ i ] and PathPointTypePathDashMode ) <> 0 then
                    iPoint.AttributeAdd( 'dashed', 'yes' ) ;
                if ( iTypes[ i ] and PathPointTypePathMarker ) <> 0 then
                    iPoint.AttributeAdd( 'marker', 'yes' ) ;
                if ( iTypes[ i ] and PathPointTypeCloseSubpath ) <> 0 then
                    iPoint.AttributeAdd( 'closed', 'yes' ) ;
            end ;
        finally
            iPoints := nil ;
            iTypes := nil ;
        end ;
    finally
        iIterator.Free ;
    end ;
end ;

// viewers

function TToXMLVisitor.VisitNode( Instance : TMedaPCBInterface ) : TObject ;
var
    i               : integer ;
begin
    fNode := fNode.NodeNew( 'PCB' ) ;
    try
        fNode.AttributeAdd( 'name', Instance.Name ) ;
        fNode.AttributeAdd( 'unit', Format( '%g', [ Instance.Base ] ) ) ;
        fNode.AttributeAdd( 'width', Format( '%g', [ Instance.Size.Width ] ) ) ;
        fNode.AttributeAdd( 'height', Format( '%g', [ Instance.Size.Height ] ) ) ;
        Result := VisitNode( TMedaViewable( Instance ) ) ;
        with Instance do
            for i := 0 to GetChildCount - 1 do
                if assigned( GetChild( i ) ) then
                    GetChild( i ).Accept( Self ) ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaPCBLayer ) : TObject ;
begin
    fNode := fNode.NodeNew( 'Layer' ) ;
    try
        fNode.AttributeAdd( 'name', Instance.Name ) ;
        fNode.AttributeAdd( 'type', Ord( Instance.LayerType ) ) ;
        Result := VisitNode( TMedaViewable( Instance ) ) ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaSchInterface ) : TObject ;
var
    i               : integer ;
begin
    fNode := fNode.NodeNew( 'Schematic' ) ;
    try
        fNode.AttributeAdd( 'name', Instance.Name ) ;
        Result := VisitNode( TMedaViewable( Instance ) ) ;
        with Instance do
            for i := 0 to GetChildCount - 1 do
                if assigned( GetChild( i ) ) then
                    GetChild( i ).Accept( Self ) ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

function TToXMLVisitor.VisitNode( Instance : TMedaSchematicPage ) : TObject ;
begin
    fNode := fNode.NodeNew( 'Page' ) ;
    try
        fNode.AttributeAdd( 'name', Instance.Name ) ;
        fNode.AttributeAdd( 'size', Instance.Size ) ;
        fNode.AttributeAdd( 'landscape', ord( Instance.Landscape ) ) ;
        Result := VisitNode( TMedaViewable( Instance ) ) ;
    finally
        fNode := fNode.Parent ;
    end ;
end ;

end.

